//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include "CTouch.h"
#include "_sprintf.h"
#include "../handwriting/CHandwriting.h"

extern Int32 g_head;
extern Int32 g_tail;
extern wchar_t g_candidateBuffer[HANDWRITING_BUFFERSIZE][11];
extern DzEvent g_handwritingEvent;

TouchData g_touchData[BUFFERSIZE];
TouchData g_buffer[BUFFERSIZE];
ZAPI_SEM g_TouchSem;

CTouch* g_touch;
static int s_IsTouchSleep = 0;

void touch_callback(Uint16 event_id, int x, int y, Uint16 *string)
{
    static int s_x1, s_y1;
    int count = 0;
    //char mybuf[50];
    if (3 != event_id) {
        //sprintf(mybuf, "!!!!!!!!!!! event_id %d\n",event_id);
        //nu_puts(mybuf);
    }
#if 0
    char buf[256];

    Uint16*  p;
    p = string;

    sprintf(buf, "!!!!!!!!!!! event_id %d",event_id);
    if (event_id == 3 || event_id == 8) {
        sprintf(buf + strlen(buf), " x:%3d, y:%3d", x, y);
    }
    else if (event_id == 9) {
        sprintf(buf + strlen(buf), " string:");
        while (*p) {
            sprintf(buf + strlen(buf), " 0x%04x", *p);
            if (*p <= 0xff) { sprintf(buf + strlen(buf), "(%c)", *(Uint8*)p); }
            p++;
        }
    }
    strcat(buf, " !!!!!!!!!!\n");
    nu_puts(buf);
//    SendTraceToPCO(buf);
#endif

    if (!g_touch->m_bRead) {
        return;
    }

    zapi_System->ObtainSem(&g_TouchSem);
    if (g_touch->m_takeBack) {
        g_touch->m_index = 1;
        g_touchData[0].dir = 0;
    }


    // buffer overflow
    if (g_touch->m_index == BUFFERSIZE) {
        switch (event_id) {
            // touch_event_normal_inking(down)
            //
            case 3:
                s_x1 = x;
                s_y1 = y;
            break;

            // touch_event_calibration
            case 8:
                nu_notifyDeviceCaller((Int32)&g_touch->m_CalibrationEvent, x << 16 | y);
                break;
        }
        //kprintf("ERROR: Touch callback buffer overflow!!\n");
        zapi_System->ReleaseSem(&g_TouchSem);
        return;
    }


    switch (event_id) {

        // touch_event_normal_inking(down)
        //
        case 3:
            s_x1 = x;
            s_y1 = y;
            g_touchData[g_touch->m_index].dir = 1;

            break;

        // touch_event_stroke_over(up)
        //
        case 4:
            g_touchData[g_touch->m_index].dir = 0;
            break;

        // touch_event_calibration
        case 8:
            if (((x != 1) && (x != 2)) || ((y != 1) && (y != 0))) {
                zapi_System->ReleaseSem(&g_TouchSem);
                return;
            }
            nu_notifyDeviceCaller((Int32)&g_touch->m_CalibrationEvent, x << 16 | y);
            zapi_System->ReleaseSem(&g_TouchSem);
            return;

        //  touch_event_get_characters
        case 9:
            g_touchData[g_touch->m_index].dir = 10;

            while((*string != L'\0') && (count < x)) {
                if (10 == count)
                    break;
                g_candidateBuffer[g_head][count] = *string;
                string++;
                count++;
            }

            if (!(g_touch->m_bValidInput)) {
                g_candidateBuffer[g_head][0] = 0x0;
            }

            g_candidateBuffer[g_head][count] = L'\0';
            g_head = (g_head + 1) % HANDWRITING_BUFFERSIZE;
            g_touch->m_bValidInput = TRUE;

            break;

        default:
            zapi_System->ReleaseSem(&g_TouchSem);
            return;
    }


    g_touchData[g_touch->m_index].x = s_x1;
    g_touchData[g_touch->m_index].y = s_y1;

    g_touchData[0].dir = g_touch->m_index;
    g_touch->m_index++;

    g_touch->m_takeBack = FALSE;
    zapi_System->ReleaseSem(&g_TouchSem);

    nu_notifyDeviceCaller((Int32)&g_touch->m_event, SIGNALED);
}


void CDECL TouchTimerRoutine(void * parameter)
{
    WaitResult wr;

    g_touch->m_mutex.Lock(&wr);

    assert(g_touch->m_head != ((g_touch->m_tail - 1) % BUFFERSIZE));

    g_touch->m_push = TRUE;
    g_touch->m_timer->Cancel();
    g_touch->m_timer->Set(0);

    assert(1 == g_buffer[(g_touch->m_head - 1) % BUFFERSIZE].dir);
    g_buffer[g_touch->m_head].x =
                    g_buffer[(g_touch->m_head - 1) % BUFFERSIZE].x;
    g_buffer[g_touch->m_head].y =
                    g_buffer[(g_touch->m_head - 1) % BUFFERSIZE].y;
    g_buffer[g_touch->m_head].dir = 3;

    g_touch->m_head = (g_touch->m_head + 1) % BUFFERSIZE;

    g_touch->m_event.NotifyByIsr();

    g_touch->m_mutex.Unlock();
}

ECode CTouch::Read(
    /* [in] */ Int64 offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf* pBuffer,
    /* [out] */ IEvent** ppCompletionEvent)
{
    TouchData* buf = (TouchData *)pBuffer->GetPayload();
    static Int32 s_x = -RANGE, s_y = -RANGE;
    Int32 x, y;
    WaitResult wr;

    if (!m_bRead) m_bRead = TRUE;

    m_mutex.Lock(&wr);
    zapi_System->ObtainSem(&g_TouchSem);

Begin:
    if (s_IsTouchSleep)
    {
        m_takeBack = TRUE;
        m_head = m_tail;
    }
    // if the nepture have the data
    //
    if (!m_takeBack) {
        assert(g_touchData[0].dir > 0 && g_touchData[0].dir < BUFFERSIZE);
        for (int i = 1; i <= g_touchData[0].dir; i++) {
            if (m_head == ((m_tail - 1) % BUFFERSIZE)) {
                kprintf("ERROR: Touch read buffer overflow!!\n");
                break;
            }
            if (m_beingChangeMode) {
                break;
            }

            InputNotifyByIsr(EVENT_TOUCH, 0x20, NULL);//NULL for temp
            x = g_touchData[i].x;
            y = g_touchData[i].y;

            // press
            //
            if (1 == g_touchData[i].dir) {
                if (m_press) {
                    // move
                    //
                    if (x < (s_x - RANGE) || x > (s_x + RANGE)
                            || y < (s_y - RANGE) || y > (s_y + RANGE)) {
                        g_buffer[m_head].dir = 2;
                        s_x = g_buffer[m_head].x = x;
                        s_y = g_buffer[m_head].y = y;
                        m_head = (m_head + 1) % BUFFERSIZE;
                        m_timer->Cancel();
                        m_timer->Set(0);
                    }
                }
                else {
                    // first press
                    //
                    if ((x < m_RecogRectX0)
                        || (x > m_RecogRectX1)
                        || (y < m_RecogRectY0)
                        || (y > m_RecogRectY1))
                    {
                        m_bValidInput = FALSE;
                    }
                    else
                    {
                        m_bValidInput = TRUE;
                    }

                    m_press = TRUE;
                    m_timer->Set(DELAYTIME);
                    m_timer->Start();
                    g_buffer[m_head].dir = 1;
                    s_x = g_buffer[m_head].x = x;
                    s_y = g_buffer[m_head].y = y;
                    m_head = (m_head + 1) % BUFFERSIZE;
                }
            }
            else if (0 == g_touchData[i].dir) {
                // release
                //

                if (m_push) {
                    g_buffer[m_head].dir = 8;
                    m_push = FALSE;
                }
                else {
                    g_buffer[m_head].dir = 0;
                }

                m_press = FALSE;
                g_buffer[m_head].x = x;
                g_buffer[m_head].y = y;
                s_x = s_y = -RANGE;
                m_head = (m_head + 1) % BUFFERSIZE;
                m_timer->Cancel();
                m_timer->Set(0);
            }
            else {
                m_timer->Cancel();
                m_timer->Set(0);

                assert(10 == g_touchData[i].dir);

                g_handwritingEvent.Notify();
            }
        }

        m_takeBack = TRUE;
    }

    if (m_head == m_tail) {
        EventState state;
        m_mutex.Unlock();
        zapi_System->ReleaseSem(&g_TouchSem);
        m_event.Wait(&wr, &state);
        if (WaitResult_Interrupted == wr) {
            return NOERROR;
        }
        m_mutex.Lock(&wr);
        zapi_System->ObtainSem(&g_TouchSem);

        goto Begin;
    }

    buf->dir = g_buffer[m_tail].dir;
    buf->x   = g_buffer[m_tail].x;
    buf->y   = g_buffer[m_tail].y;
    // InputNotifyByIsr(EVENT_TOUCH, 0x20, buf);//NULL for temp
    m_tail = (m_tail + 1) % BUFFERSIZE;

    m_mutex.Unlock();
    zapi_System->ReleaseSem(&g_TouchSem);

    return NOERROR;
}

void CTouch::InitTouchState()
{
    m_beingChangeMode = TRUE;
    g_touchData[0].dir = 0;
    m_takeBack = 1;
    m_index = 1;
    m_tail = 0;
    m_head = 0;
    m_push = FALSE;
    m_press = FALSE;
    m_timer->Cancel();
    m_timer->Set(0);
    m_beingChangeMode = FALSE;
}

ECode CTouch::Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf &inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    WaitResult wr;
    EventState state;
    ECode ec = NOERROR;

    if (!m_bRead) m_bRead = TRUE;

    Int32* data = (Int32 *)inBuffer.GetPayload();

kprintf("in touch: control code = %d\n", nControlCode);

    switch(nControlCode) {
        case 0: //set recognization mode
            zapi_Touch->SetMode(0);
            break;

        case 1: //set graphic mode
            zapi_Touch->SetMode(1);
            break;

        case 2: //recognize immediately
            zapi_Touch->Start_Rec();
            break;

        case 3: //set pen-up waiting time
            zapi_Touch->SetRecTime(Uint8(*data));
            break;

        case 7:
            //Enable or Disable touch sleep.TRUE touch sleep, FALSE touch wake
            if (inBuffer.GetCapacity() < (int)sizeof(Bool32)) {
                return E_INVALID_ARGUMENT;
            }
            //kprintf("CTouch::Control:*(Bool32 *)inBuffer.GetPayload()=%d\n", *((Bool32 *)inBuffer.GetPayload()));
            InitTouchState();
            zapi_Touch->SleepEnable(*((Bool32 *)inBuffer.GetPayload()));
            if (*((Bool32 *)inBuffer.GetPayload())) {
                s_IsTouchSleep = 1;
            }
            else
            {
                s_IsTouchSleep = 0;
            }
            break;
        case 11: //recognize chinese characters
            zapi_Touch->SetRecType(0x0300);
            break;

        case 12: // recognize english letters(include Capital letter)
            zapi_Touch->SetRecType(0x0400 | 0x0001);
            break;

        case 13: // recognize numbers
            zapi_Touch->SetRecType(0x0800);
            break;

        case 14: // recognize symbols
            zapi_Touch->SetRecType(0x1000);
            break;

        case 99:
            m_RecogRectX0 = *data;
            m_RecogRectY0 = *(++data);
            m_RecogRectX1 = *(++data);
            m_RecogRectY1 = *(++data);

kprintf("RectX0 = %d, RectY0 = %d, RectX1 = %d, RectY1 = %d-----\n",
        m_RecogRectX0, m_RecogRectY0, m_RecogRectX1, m_RecogRectY1);

            break;

        case 100:
            m_RecogRectX0 = 0;
            m_RecogRectY0 = 0;
            m_RecogRectX1 = 0;
            m_RecogRectY1 = 0;
            break;

        case 2000:
            zapi_Touch->SleepEnable(TRUE);
            InitTouchState();
            s_IsTouchSleep = 1;
            break;

        case 2001:
            InitTouchState();
            zapi_Touch->SleepEnable(FALSE);
            s_IsTouchSleep = 0;
            break;

        // Query
        case 8000:
            if (pOutBuffer == NULL
                || pOutBuffer->GetCapacity() < (Int32)sizeof(Int32)) {
                return E_INVALID_ARGUMENT;
            }
            *(Int32*)pOutBuffer->GetPayload() = zapi_Touch->Calibration_Query();
            pOutBuffer->SetUsed(sizeof(Int32));
            break;

        // Start
        case 8001:
            InitTouchState();
            zapi_Touch->Calibration_Start();
            m_CalibrationPoint = 1;
            break;

        // Wait result
        case 8002:
            if (pOutBuffer == NULL
                || pOutBuffer->GetCapacity() < (Int32)(2 * sizeof(Int32))) {
                return E_INVALID_ARGUMENT;
            }
            g_touch->m_CalibrationEvent.Wait(&wr, &state);
            if (wr == WaitResult_OK) {
                InputNotifyByIsr(EVENT_TOUCH, 0x20, NULL);
                m_head = 1;
                m_tail = 0;
                g_buffer[m_tail].dir = 0;
                g_buffer[m_tail].x = 0;
                g_buffer[m_tail].y = 0;
                m_event.Notify(1);

                Int32 point = state >> 16;
                Int32 bIsOK = state & 0xffff;

                if (m_CalibrationPoint == 1 && point == 2) {
                    // Lost point 1 succeeded event, make it
                    point = 1;
                    bIsOK = 1;
                }

                *(Int32*)pOutBuffer->GetPayload() = point;
                *((Int32*)pOutBuffer->GetPayload() + 1) = bIsOK;
                pOutBuffer->SetUsed(2 * sizeof(Int32));

                if (point == 1 && bIsOK == 1) {
                    m_CalibrationPoint = 2;
                }
            }
            else if (wr == WaitResult_Interrupted) {
                ec = E_INTERRUPTED;
            }
            else {
                ec = E_FAIL;
            }
            break;

        // End
        case 8003:
            InitTouchState();
            m_CalibrationPoint = -1;
            zapi_Touch->Calibration_End();
            break;
        default:
            kprintf("CTouch::Control control code %d not implement!\n", nControlCode);
            ec = E_ACCESS_DENIED;
            break;
    };

    return ec;
}

EXTERN IDeviceDriver * CDECL CreateTouch(uint_t uDeviceNo, void *pvParameter)
{
    g_touch = new CTouch;
    if (!g_touch) {
        DRV_EXPRESSION(ERRORLEVEL, kprintf("not enough memory\n"));

        return NULL;
    }

    ECode ec = g_touch->Init();
    if (DRV_FAILED(ec)) {
        return NULL;
    }

    zapi_Touch->Register(&touch_callback);
    zapi_System->CreateSem(&g_TouchSem, (char *)"TouchSem", 1);

    return g_touch;
}
